import { Injectable } from '@angular/core';
import { Store } from '@ngxs/store';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzModalService } from 'ng-zorro-antd/modal';
import { Subject } from 'rxjs';
import { EntityDetailActions, EntityStateService } from '../../core';
import { IdentityRoleDto, IdentityUserDto, IdentityUserService } from '../proxy/identity';
import {
  IdentityUserDetailState,
  SetCurrentUserRoleNames,
  SetDefaultRoleNames,
} from './user-detail.state';

import snq from 'snq';
import { Router } from '@angular/router';

@Injectable({
  providedIn: 'root',
})
export class IdentityUserDetailService extends EntityStateService<IdentityUserDto> {
  public onSearch(input: any): Promise<IdentityUserDto> {
    return this.modelService.findByUsername(input).toPromise();
  }
  private roleNames$ = new Subject<string[]>();
  public roles: Array<{ label: string; value: string; checked?: boolean; model: IdentityRoleDto }>;

  public get roleNames() {
    return this.store.selectSnapshot(IdentityUserDetailState.getRoleNames);
  }

  public get onRoleNamesChanged() {
    return this.roleNames$.asObservable();
  }

  constructor(
    protected store: Store,
    protected modal: NzModalService,
    protected message: NzMessageService,
    protected modelService: IdentityUserService,
    public router: Router
  ) {
    super(IdentityUserDetailState, store, modal, message);

    this.store
      .select(IdentityUserDetailState.getRoleNames)
      .subscribe(roleNames => this.roleNames$.next(roleNames));
  }

  public changeToEmpty(): Promise<any> {
    return super.changeToEmpty().then(() => this.initRoles());
  }

  public giveupToEmpty(): Promise<any> {
    return super.giveupToEmpty().then(() => this.initRoles());
  }

  public changeToView(id?: string): Promise<IdentityUserDto> {
    return new Promise((resolve, reject) => {
      this.checkDirty().then(() =>
        this.store
          .dispatch([
            !!id
              ? EntityDetailActions.ChangeToViewNewModel(this.storeClass, id)
              : EntityDetailActions.ChangeToView(this.storeClass),
            new SetCurrentUserRoleNames(id),
          ])
          .subscribe(() => {
            resolve(this.currentModel);
          })
      );
    });
  }

  public giveupToView(): Promise<any> {
    return super.giveupToView().then(() => this.setCurrentUserRoleNames(this.currentModel.id));
  }

  public navigateToList(): Promise<boolean> {
    return this.router.navigate(['identity/users']);
  }

  public save(input: any): Promise<IdentityUserDto> {
    return super.save(input).then(model => this.setCurrentUserRoleNames(model.id));
  }

  public setCurrentUserRoleNames(id: string): Promise<any> {
    return this.store.dispatch(new SetCurrentUserRoleNames(id)).toPromise();
  }

  public setDefaultRoleNames(roleNames: string[]): Promise<any> {
    return this.store.dispatch(new SetDefaultRoleNames(roleNames)).toPromise();
  }

  public getRoles(): Promise<IdentityRoleDto[]> {
    return new Promise((resolve, reject) => {
      this.modelService.getAssignableRoles().subscribe(result => {
        resolve(result.items);
      });
    });
  }

  public initRoles(): Promise<IdentityRoleDto[]> {
    return new Promise((resolve, reject) => {
      this.getRoles().then(items => {
        this.roles = snq(() =>
          items.map(role => {
            return { label: role.name, value: role.id, checked: role.isDefault, model: role };
          })
        );
        this.setDefaultRoleNames(
          snq(() => items.filter(role => role.isDefault).map(x => x.name))
        ).then(() => resolve(items));
      });
    });
  }

  public roleNamesEquals(formRoleNames: string[]): boolean {
    if (!formRoleNames?.length && !this.roleNames?.length) return true;

    for (var formRoleName of formRoleNames) {
      if (this.roleNames.indexOf(formRoleName) == -1) return false;
    }

    for (var stateRoleName of this.roleNames) {
      if (formRoleNames.indexOf(stateRoleName) == -1) return false;
    }

    return true;
  }
}
